package com.alks.function.service.impl.stockdept.pcfactorystockquery;

import com.alibaba.excel.util.StringUtils;
import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.entity.data.entity.PcSlipDetailPartBf;
import com.alks.entity.data.entity.PcSlipTempPartBf;
import com.alks.entity.data.entity.SdMaterialPart;
import com.alks.entity.data.entity.pcMtlAccPartBf;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.stockdept.pcfactorystockwork.CsmDetailDto;
import com.alks.function.data.dto.stockdept.pcfactorystockwork.CsmHeadDto;
import com.alks.function.data.request.stockdept.pcfactorystockwork.CsmDetAddRequest;
import com.alks.function.data.request.stockdept.pcfactorystockwork.CsmDetRequest;
import com.alks.function.mapper.stockdept.pcfactorystockquery.*;
import com.alks.function.service.stockdept.pcfactorystockquery.IConsuOutService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.alks.entity.data.enums.ResultCodeEnum.DATA_IS_ERR;
import static com.alks.entity.data.enums.ResultCodeEnum.INPUT_ERROR;

@Service
@Slf4j
@RequiredArgsConstructor
public class ConsuOutServiceImpl extends ServiceImpl<SD_SQ_PcSlipTempPartBfMapper, PcSlipTempPartBf> implements IConsuOutService {
    private final SD_SQ_PcMtlAccPartBfMapper accMapper;
    private final SD_SQ_PcSlipDetailPartBfMapper detailMapper;
    private final SD_SQ_PcSlipPartReasonMapper reasonMapper;
    private final SD_SQ_PcSlipTempPartBfMapper tempMapper;
    private final SD_SQ_SdMaterialPartMapper materialMapper;
    private final RedisService redisService;

    @Override
    public ResponseInfo getCsmOutTempQuery() {
        /*查询数据*/
        List<PcSlipTempPartBf> temp = lambdaQuery()
                .eq(PcSlipTempPartBf::getSysUser, UserIdThread.get().getLoginName())
                .eq(PcSlipTempPartBf::getSlipType, "O")
                .eq(PcSlipTempPartBf::getCompanyId, "BF").list();
        List<CsmHeadDto> dtos = BeanUtil.copyList(temp, CsmHeadDto.class);
        /*补充库存数据*/
        List<String> mtlNos = dtos.stream().map(CsmHeadDto::getMtlNo).collect(Collectors.toList());
        LambdaQueryWrapper<pcMtlAccPartBf> wrapper = new LambdaQueryWrapper<pcMtlAccPartBf>()
                .select(pcMtlAccPartBf::getMtlNo, pcMtlAccPartBf::getStkQty)
                .in(pcMtlAccPartBf::getMtlNo, mtlNos);
        List<pcMtlAccPartBf> accs = accMapper.selectList(wrapper);
        dtos.forEach(dto -> {
            // 在accs集合中找到与dto相同mtlNo的实体类
            accs.stream()
                    .filter(acc -> acc.getMtlNo().equals(dto.getMtlNo())).findFirst()
                    .ifPresent(acc -> dto.setStkQty(acc.getStkQty()));  // 如果找到匹配的acc，则将其stkQty赋值给dto
        });
        return ResponseInfo.ok(dtos);
    }

    @Override
    public ResponseInfo csmOutTempAdd(String mtlNo) {
        /*判断数据是否重复*/
        PcSlipTempPartBf temps = lambdaQuery()
                .eq(PcSlipTempPartBf::getMtlNo, mtlNo)
                .eq(PcSlipTempPartBf::getSysUser, UserIdThread.get().getLoginName())
                .eq(PcSlipTempPartBf::getSlipType, "O").one();
        if (temps != null) {
            return ResponseInfo.error("已有重复数据，请勿重复添加", INPUT_ERROR.getCode());
        }
        /*获取数据*/
        LambdaQueryWrapper<SdMaterialPart> wrapper = new LambdaQueryWrapper<SdMaterialPart>()
                .eq(SdMaterialPart::getMtlNo, mtlNo)
                .eq(SdMaterialPart::getCompanyId, "BF");
        SdMaterialPart part = materialMapper.selectOne(wrapper);
        if (part == null || BeanUtil.allIsEmpty(part)) {
            log.error("请求方法：csmOutTempAdd\n使用的表：SD_MATERIAL_PART\n错误原因：没有找到参数" +
                    "\n请求参数：mtlNo:{} ", mtlNo);
            return ResponseInfo.error("数据异常，请联系数据库管理员", DATA_IS_ERR.getCode());
        }
        /*提取并写入数据*/
        PcSlipTempPartBf temp = BeanUtil.copyBean(part, PcSlipTempPartBf.class);
        temp.setStockNo("X2");
        temp.setSysUser(UserIdThread.get().getLoginName());
        temp.setSysDate(LocalDateTime.now());
        temp.setSlipType("O");
        temp.setItem(1);
        tempMapper.insert(temp);
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo csmOutTempUpdate(String mtlNo, BigDecimal qty, String remark) {
        /*判断库存量是否充足*/
        LambdaQueryWrapper<pcMtlAccPartBf> wrapper = new LambdaQueryWrapper<pcMtlAccPartBf>()
                .select(pcMtlAccPartBf::getStkQty)
                .eq(pcMtlAccPartBf::getMtlNo, mtlNo);
        pcMtlAccPartBf partBf = accMapper.selectOne(wrapper);
        if (partBf==null){
            return ResponseInfo.error("库存量不足", INPUT_ERROR.getCode());
        }
        BigDecimal stkQty = partBf.getStkQty() == null ? BigDecimal.ZERO : partBf.getStkQty();
        if (qty.compareTo(stkQty) > 0) {
            return ResponseInfo.error("库存量不足", INPUT_ERROR.getCode());
        }
        /*更新数据*/
        lambdaUpdate().set(PcSlipTempPartBf::getQty, qty)
                .set(PcSlipTempPartBf::getRemark, remark)
                .eq(PcSlipTempPartBf::getMtlNo, mtlNo)
                .eq(PcSlipTempPartBf::getSysUser, UserIdThread.get().getLoginName())
                .eq(PcSlipTempPartBf::getSlipType, "O").update();
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo csmOutTempDelete(String mtlNo) {
        /*删除对应数据*/
        LambdaQueryWrapper<PcSlipTempPartBf> wrapper = new LambdaQueryWrapper<PcSlipTempPartBf>()
                .eq(PcSlipTempPartBf::getMtlNo, mtlNo)
                .eq(PcSlipTempPartBf::getCompanyId, "BF")
                .eq(PcSlipTempPartBf::getSysUser, UserIdThread.get().getLoginName())
                .eq(PcSlipTempPartBf::getSlipType, "O");
        tempMapper.delete(wrapper);
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo getCsmOutDetQuery(CsmDetRequest request) {
        /*查询数据*/
        LambdaQueryWrapper<PcSlipDetailPartBf> wrapper = new LambdaQueryWrapper<PcSlipDetailPartBf>()
                .eq(request.getSlipNo() != null && !request.getSlipNo().isEmpty(), PcSlipDetailPartBf::getSlipNo, request.getSlipNo())
                .eq(request.getSlipReason() != null && !request.getSlipReason().isEmpty(), PcSlipDetailPartBf::getSlipReason, request.getSlipReason())
                .eq(request.getVendorNo() != null && !request.getVendorNo().isEmpty(), PcSlipDetailPartBf::getDeptNo, request.getVendorNo())
                .ge(request.getSlipDateStart() != null, PcSlipDetailPartBf::getSlipDate, request.getSlipDateStart())
                .le(request.getSlipDateEnd() != null, PcSlipDetailPartBf::getSlipDate, request.getSlipDateEnd())
                .ge(request.getSysDateStart() != null, PcSlipDetailPartBf::getSysDate, request.getSysDateStart())
                .le(request.getSysDateEnd() != null, PcSlipDetailPartBf::getSysDate, request.getSysDateEnd())
                .eq(request.getMtlName() != null && !request.getMtlName().isEmpty(), PcSlipDetailPartBf::getMtlName, request.getMtlName())
                .eq(request.getLotNo() != null && !request.getLotNo().isEmpty(), PcSlipDetailPartBf::getLotNo, request.getLotNo())
                .eq(PcSlipDetailPartBf::getSysUser, UserIdThread.get().getLoginName())
                .eq(PcSlipDetailPartBf::getSlipType, "O");
        List<PcSlipDetailPartBf> detail = detailMapper.selectList(wrapper);
        /*提取数据*/
        List<CsmDetailDto> dtos = BeanUtil.copyList(detail, CsmDetailDto.class);
        dtos.stream().forEach(a -> a.setSysUser(UserIdThread.get().getUserName()));
        return ResponseInfo.ok(dtos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseInfo csmOutDetAdd(CsmDetAddRequest request) {
        /*检测入参健壮性*/
        csmOutDetAddRequestCheck(request);
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        /*查询数据*/
        List<PcSlipTempPartBf> temps = lambdaQuery()
                .eq(PcSlipTempPartBf::getCompanyId, companyId)
                .eq(PcSlipTempPartBf::getSysUser, tokenDTO.getLoginName())
                .eq(PcSlipTempPartBf::getSlipType, "O")
                .gt(PcSlipTempPartBf::getQty, 0).list();
        /*判断是否存在*/
        if (ArrayUtils.isEmpyt(temps)) {
            return ResponseInfo.error("没有数量大于零的数据，出库失败", INPUT_ERROR.getCode());
        }
        Map<String, BigDecimal> mtlQtys = temps.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(
                        PcSlipTempPartBf::getMtlNo,
                        a -> Optional.ofNullable(a.getQty()).map(BigDecimal::valueOf).orElse(BigDecimal.ZERO),
                        BigDecimal::add
                ));
        List<String> mtlNos = new ArrayList<>(mtlQtys.keySet());
        /*判断库存是否充足并出库处理*/
        List<Map<String, Object>> stkQtys = accMapper.getStkQty(mtlNos, companyId);
        if (ArrayUtils.isEmpyt(stkQtys)) {
            return ResponseInfo.error("没有库存，出库失败", INPUT_ERROR.getCode());
        }
        Map<String, BigDecimal> stkQtyMap = stkQtys
                .stream().filter(Objects::nonNull)
                .collect(Collectors.toMap(a -> (String) a.get("mtlNo"),
                        a -> Optional.ofNullable((BigDecimal)a.get("stkQty")).orElse(BigDecimal.ZERO),BigDecimal::add));
        for (PcSlipTempPartBf temp : temps) {
            if (stkQtyMap.get(temp.getMtlNo())==null||stkQtyMap.get(temp.getMtlNo()).compareTo(BigDecimal.valueOf(temp.getQty())) < 0) {
                throw new ServiceErrorException("出库失败，有材料库存余量不足,物料编号为：" + temp.getMtlNo());
            }
        }
        /*库存出库处理*/
        accMapper.stkOutByMap(mtlQtys,companyId);
        List<PcSlipDetailPartBf> details = BeanUtil.copyList(temps, PcSlipDetailPartBf.class);
        /*补充参数*/
        String slipNo = redisService.slipNoCreat("X");
        for (PcSlipDetailPartBf detail : details) {
            detail.setSlipNo(slipNo);
            detail.setDeptNo(request.getDeptNo());
            detail.setDeptName(request.getDeptName());
            detail.setVendorNo(request.getVendorNo());
            detail.setVendorName(request.getVendorName());
            detail.setSlipDate(request.getSlipDate().atStartOfDay());
            detail.setSlipReason(request.getSlipReason());
            detail.setSysUser(tokenDTO.getLoginName());
            detail.setSysDate(request.getSysDate() == null ? LocalDateTime.now() : request.getSysDate().atStartOfDay());
            detail.setSendNo(request.getSendNo());
            /*写入明细数据*/
            detailMapper.insert(detail);
        }
        /*删除原标数据*/
        LambdaQueryWrapper<PcSlipTempPartBf> wrapper = new LambdaQueryWrapper<PcSlipTempPartBf>()
                .eq(PcSlipTempPartBf::getCompanyId, companyId)
                .eq(PcSlipTempPartBf::getSysUser, tokenDTO.getLoginName())
                .eq(PcSlipTempPartBf::getSlipType, "O");
        tempMapper.delete(wrapper);
        return ResponseInfo.ok();
    }

    /**
     * 入库批处理检测
     * @param request
     */
    private void csmOutDetAddRequestCheck(CsmDetAddRequest request) {
        if (StringUtils.isBlank(request.getSlipReason())){
            throw new ServiceErrorException("出入库原因不能为空");
        }
        if (StringUtils.isBlank(request.getVendorNo())&&StringUtils.isBlank(request.getDeptNo())){
            throw new ServiceErrorException("出入库厂商和部门不能同时为空");
        }
    }

    @Override
    public ResponseInfo csmOutDetailDelete(String mtlNo, String slipNo) {
        /*删除数据*/
        LambdaQueryWrapper<PcSlipDetailPartBf> wrapper = new LambdaQueryWrapper<PcSlipDetailPartBf>()
                .eq(PcSlipDetailPartBf::getMtlNo, mtlNo)
                .eq(PcSlipDetailPartBf::getSlipNo, slipNo)
                .eq(PcSlipDetailPartBf::getSysUser, UserIdThread.get().getLoginName())
                .eq(PcSlipDetailPartBf::getSlipType, "O");
        detailMapper.delete(wrapper);
        return ResponseInfo.ok();
    }
}
